using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using System;
using System.Collections.Generic;

namespace Microsoft.SqlServer.Management.SqlManagerUI.Scripting
{
    using Server = Microsoft.SqlServer.Management.Smo.Server;
	internal class DependencyDiscovery
	{
		public Urn[] GetCreateDependencies(Server server, IEnumerable<Urn> urns, bool isDataOnly)
		{
			return DependencyDiscovery.ProcessDependencyChain(server, this.SubmitDependencyRequest(server, urns), isDataOnly);
		}

		protected DependencyChainCollection SubmitDependencyRequest(Server server, IEnumerable<Urn> urns)
		{
			DependencyRequest dependencyRequest = new DependencyRequest();
			dependencyRequest.Urns = DependencyDiscovery.GetUrns(urns);
			dependencyRequest.ParentDependencies = true;
			object obj = ReflectionUtilities.InvokeMethod(server, "GetExecutionManager", null);
			object obj2 = ReflectionUtilities.InvokeMethod(obj, "GetDependencies", new object[]
			{
				dependencyRequest
			});
			return (DependencyChainCollection)obj2;
		}

		protected static Urn[] ProcessDependencyChain(Server server, DependencyChainCollection dependencyChain, bool isDataOnly)
		{
			List<Dependency[]> cycles = DependencyDiscovery.FindCycles(dependencyChain);
			DependencyDiscovery.BreakCycles(server, cycles, isDataOnly);
			List<Urn> list = new List<Urn>(dependencyChain.Count);
			foreach (Dependency dependency in dependencyChain)
			{
				DependencyDiscovery.AddDependency(list, dependency);
			}
			return list.ToArray();
		}

		private static void AddDependency(List<Urn> objectsInOrder, Dependency dependency)
		{
			if (dependency == null || objectsInOrder.Contains(dependency.Urn))
			{
				return;
			}
			foreach (Dependency dependency2 in dependency.Links)
			{
				DependencyDiscovery.AddDependency(objectsInOrder, dependency2);
			}
			objectsInOrder.Add(dependency.Urn);
		}

		private static List<Dependency[]> FindCycles(DependencyChainCollection dependencyChain)
		{
			List<Dependency[]> list = new List<Dependency[]>();
			List<Urn> list2 = new List<Urn>();
			foreach (Dependency dependency in dependencyChain)
			{
				if (!list2.Contains(dependency.Urn))
				{
					List<Dependency> currentChain = new List<Dependency>();
					DependencyDiscovery.Visit(dependency, currentChain, list, list2);
				}
			}
			return list;
		}

		private static void BreakCycles(Server server, List<Dependency[]> cycles, bool isDataOnly)
		{
			foreach (Dependency[] current in cycles)
			{
				int num = -1;
				int num2;
				for (int i = 0; i < current.Length; i++)
				{
					string text = current[i].Urn.Type.Trim();
					if (text.Equals("StoredProcedure", StringComparison.OrdinalIgnoreCase) || text.Equals("Synonym", StringComparison.OrdinalIgnoreCase))
					{
						num = i;
						break;
					}
					if (text.Equals("UserDefinedFunction", StringComparison.OrdinalIgnoreCase))
					{
						SqlSmoObject smoObject = server.GetSmoObject(current[i].Urn);
						UserDefinedFunction userDefinedFunction = smoObject as UserDefinedFunction;
						if (userDefinedFunction != null && userDefinedFunction.FunctionType == UserDefinedFunctionType.Scalar && !userDefinedFunction.IsSchemaBound)
						{
							num = i;
							break;
						}
					}
					if (isDataOnly)
					{
						if (!text.Equals("Table", StringComparison.OrdinalIgnoreCase))
						{
							num = i;
							break;
						}
					}
					else
					{
						num2 = i + 1;
						if (num2 >= current.Length)
						{
							num2 = 0;
						}
						if (text.Equals("Table", StringComparison.OrdinalIgnoreCase) && current[num2].Urn.Type.Trim().Equals("Table", StringComparison.OrdinalIgnoreCase))
						{
							num = i;
							break;
						}
					}
				}
				if (num < 0)
				{
					if (isDataOnly)
					{
						throw new CyclicalForeignKeyException(ScriptingEngineResources.ERROR_CyclicalForeignKeys);
					}
					num = 0;
				}
				num2 = num + 1;
				if (num2 >= current.Length)
				{
					num2 = 0;
				}
				if (current[num].Links.Contains(current[num2]))
				{
					current[num].Links.Remove(current[num2]);
				}
			}
		}

		private static void Visit(Dependency dependency, List<Dependency> currentChain, List<Dependency[]> cycles, List<Urn> visitedUrns)
		{
			if (currentChain.Contains(dependency))
			{
				List<Dependency> list = new List<Dependency>();
				int num = currentChain.IndexOf(dependency);
				for (int i = num; i < currentChain.Count; i++)
				{
					list.Add(currentChain[i]);
				}
				foreach (Dependency current in list)
				{
					if (!visitedUrns.Contains(current.Urn))
					{
						cycles.Add(list.ToArray());
						break;
					}
				}
				return;
			}
			currentChain.Add(dependency);
			foreach (Dependency dependency2 in dependency.Links)
			{
				if (dependency2 != null && dependency2 != dependency)
				{
					DependencyDiscovery.Visit(dependency2, currentChain, cycles, visitedUrns);
				}
			}
			currentChain.RemoveAt(currentChain.Count - 1);
			visitedUrns.Add(dependency.Urn);
		}

		protected static Urn[] GetUrns(IEnumerable<Urn> urns)
		{
			FilterObjectWithDepends dependencyFilter = DependencyDiscovery.GetDependencyFilter();
			return dependencyFilter.GetDiscoverableObjects(urns);
		}

		protected static FilterObjectWithDepends GetDependencyFilter()
		{
			return new FilterObjectWithDepends();
		}
	}
}
